rescue に Exception or StandardError を指定してはいけない理由を調べる
2024/4/19
rescue_fromでExceptionやStandardErrorを指定すると、Railsでの正しい例外ハンドリングが阻害されて深刻な副作用が生じる可能性があります。よほどの理由がない限り、このような指定はおすすめできません。
深刻な副作用って具体的には何?と気になったので調べてみる
ハンドラの登録順をミスるとやばい
評価は下から上にされる
これは ドキュメント にも記載されているが、登録したハンドラは下から上の順に検索され、例外がマッチしたらそこで終了する。マッチするかどうかは exception.is_a?(klass) でチェックする。
マッチする例外がない場合 cause を取り出して再度評価する
発生した例外にマッチするハンドラがなかった場合 cause でラップされた元の例外を取り出して再度すべてのハンドラを調査するという挙動をする。
Exception を以下のようにハンドラ登録順を一番下に指定すると、全ての例外が Exception に吸収されて上のハンドラは通らずに終わる
code:ruby
class ApplicationController < ActionController::Base
rescue_from User::NotAuthorized, with: :deny_access
rescue_from ActiveRecord::RecordInvalid, with: :show_record_errors
rescue_from StandardError, with :other_error_handler # ほとんどの例外がここに吸収される
# ...
end
設定するとしたら「上」に持っていく必要がある。記載順が変わるだけで容易に壊れてしまう恐れがあるので、今後のメンテナンス性にも注意する必要がある。
ラップされた例外を取り出せなくなる
ラップされている例外が cause で取り出されていた挙動が、Exception のハンドラを登録することで必ず起こらなくなる。
これまでは 1 周目の rescue_handlers の検索で ActiveRecord::ConnectionNotEstablished にマッチするハンドラが見つからず、cause で Mysql2::Error::ConnectionError が取り出され、2 周目で :show_internal_server_error にマッチしていた
変更後は 1 周目で ActiveRecord::ConnectionNotEstablished が :other_error_handler にマッチしそこで処理されるように変わった
Exception を置いてると1週目の段階で ActiveRecord::ConnectionNotEstablished がマッチされてしまう
本来は2週目のcauseで Mysql2::Error::ConnectionErrorを取り出してマッチしたいはずが先にとられてしまう
記述に気を付ければ一応使えはするけど、想定しない挙動が出てくる可能性がそこそこあるのであんまり使うもんじゃないよねという感じという理解
古めのコード見てるとよくわからんシステムエラー含めて StandardErrorで全部キャッチしてユーザーに対しては500エラー用のメッセージを出す設計を見かけるけど、もしかしてあまりお行儀がよろしいやり方ではない……?と気になり始めた
(まだ答えは出ていない)
この人のまとめ方好感持てる
Rails では 例外処理作るときはどこに入れたらいいのか?について調べてみるとコントローラー以外の方が良いよねという意見が複数あった
なんとなく「こうするのはあんまりよくない」的な温度感を知ることはできたが、ちゃんとした答えは自分の中で持ててない気がしてまた調べよ~~となった
その後
気になりすぎて髪乾かしながら調べてたらここに大体気になってる話が書いてあったので発表含めてちゃんと追うことにする
https://youtu.be/zcT5GufiQ-0?si=fa9hKCBZKUPeqVtu
ふと浮かんだツイート
https://gyazo.com/6d6205fef2f44909ed6bfac43b66c1c4